home *** CD-ROM | disk | FTP | other *** search
/ Magnum One / Magnum One (Mid-American Digital) (Disc Manufacturing).iso / d26 / asmtut4.arc / CHAP19-1.DOC next >
Text File  |  1990-08-10  |  22KB  |  513 lines

  1.  
  2.  
  3.  
  4.                                                                            191
  5.  
  6.                                   CHAPTER 19 - STRINGS
  7.  
  8.  
  9.              Sometimes we want to deal with long strings of information. Here
  10.              long means hundreds or thousands of bytes, not tens of bytes. The
  11.              8086 provides a group of instructions to move and compare
  12.              strings. These instructions have a rigid structure, but with a
  13.              little bit of effort we can get them to work easily for us. We
  14.              will start with SCAS, since it is simple, yet embodies all the
  15.              rigid features of these instructions. 
  16.  
  17.              SCAS (scan string) compares either a byte to AL or a word to AX.
  18.              The byte or word must be in memory, and the register must be AL
  19.              or AX. SCAS also increments or decrements the pointer. First, the
  20.              size:
  21.  
  22.                  scasb
  23.  
  24.              compares a byte to AL, while:
  25.  
  26.                  scasw
  27.  
  28.              compares a word to AX. But where's the pointer? You have no
  29.              choice, it's DI. Not only is it DI, but it MUST be ES:DI. The ES
  30.              segment is coded into the 8086 microcode; the DI register is
  31.              coded into the 8086 microcode; there is nothing you can do to
  32.              change it. What about incrementing or decrementing? In the flags
  33.              register, there is a flag called the direction flag. It is set
  34.              manually by the program. If DF = 0, SCAS increments DI; if DF =
  35.              1, SCAS decrements DI.{1} The equivalent software for the
  36.              instruction would be:
  37.  
  38.                            (scasb)                       (scasw)
  39.              DF = 0        cmp  al, es:[di]              cmp  ax, es:[di]
  40.                            pushf                         pushf
  41.                            add  di, 1                    add  di, 2
  42.                            popf {2}                      popf
  43.  
  44.  
  45.              DF = 1        cmp  al, es:[di]              cmp  ax, es:[di]
  46.                            pushf                         pushf
  47.                            sub  di, 1                    sub  di, 2
  48.                            popf                          popf
  49.  
  50.              ____________________
  51.  
  52.                 1. Every time you have called show_regs DF has been there; it
  53.              doesn't show 0 and 1, it shows + and -  (+ = 0 , - = 1).
  54.  
  55.                 2. The microcode doesn't really push and pop the flags. This
  56.              is only to indicate that the order of operations is (1) get the
  57.              byte (word) from the string, (2) compare and set the flags, and
  58.              finally (3) increment (decrement) the pointer without changing
  59.              any of the flags.
  60.  
  61.              ______________________
  62.  
  63.              The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson
  64.  
  65.  
  66.  
  67.  
  68.              The PC Assembler Tutor                                        192
  69.              ______________________
  70.  
  71.              Thus, at the end of the end of the instruction, DI is in a new
  72.              place and the flags are set according to the compare result. DI
  73.              is incremented by a byte for the byte instructions; it is
  74.              incremented by a word for the word instructions. The same pattern
  75.              holds true for decrementing DI. 
  76.  
  77.              We set the direction flag with the instruction STD (set direction
  78.              flag) and we clear the direction flag by using CLD (clear
  79.              direction flag). It only needs to be set or cleared once, and
  80.              this should be done before starting the operation. DF is only
  81.              changed by those specific instructions from the program - it
  82.              can't be changed by any arithmetical or logical operation on the
  83.              chip.
  84.  
  85.              If you have a string and you are looking for a specific number,
  86.              (27 for instance), you simply put that number in AL (or AX) and
  87.              run a loop. If:
  88.  
  89.                   long_string  db 5000 dup (?) 
  90.  
  91.              contains data and we want to look for a 27d then the operation
  92.              is:
  93.  
  94.                  lea  di, long_string{3}
  95.                  mov  al, 27
  96.                  cld
  97.  
  98.              search_loop:
  99.                  scasb
  100.                  jne  search_loop
  101.  
  102.              on exiting, DI will point 1 PAST the matching byte (word). You
  103.              move back one byte (word) to find the match. Why would anyone
  104.              want this instruction? With a 0 in AL, it will find the end of a
  105.              C (0d terminated) string quickly. Also, that number 27 is no
  106.              accident. 27d is the ASCII escape character. For a lot of
  107.              hardware, 27d indicates that the bytes that follow are not ASCII
  108.              characters but are technical information. For instance, on my
  109.              printer the sequence (27d, 65d, 0d, 11d) sets tabs every 11
  110.              columns. SCAS can find where these substrings are so the program
  111.              can operate on them. 
  112.  
  113.              In order to use string instructions, we need strings to work on.
  114.              The one we will use is called CH1STR.OBJ. It is in \XTRAFILE.  It
  115.              is an object file that contains one data segment containing a
  116.              string of lower case characters. The string is several thousand
  117.              bytes long, it is terminated by 0, and it contains ONLY the lower
  118.              case letters (a-z). It is the first draft of part of chapter 0
  119.              with all punctuation, numbers, spaces, carriage returns etc.
  120.              deleted. All upper case letters have been converted to lower case
  121.              so we don't have to worry about the difference between A and a, Q
  122.              and q. 
  123.  
  124.              The name of the array in CH1STR.OBJ is CH1STR and it is defined:
  125.              ____________________
  126.  
  127.                 3. Assuming that long_string's segment address is in ES.
  128.  
  129.  
  130.  
  131.  
  132.              Chapter 19 - Strings                                          193
  133.              ____________________
  134.  
  135.  
  136.                  PUBLIC  ch1str
  137.  
  138.              so that you can access it with the SEG and OFFSET operators.
  139.              First, let's find out how long the string is.{4} 
  140.  
  141.              MYPROG1.ASM
  142.              ; - - - - - - - - - -
  143.              STRINGSTUFF  SEGMENT  PUBLIC 'DATA' 
  144.              EXTRN  ch1str:BYTE
  145.              STRINGSTUFF  ENDS
  146.              ; - - - - - - - - - - 
  147.              ;- - - - - - - - - - PUT CODE BELOW THIS LINE
  148.  
  149.                  mov  ax, seg ch1str      ; segment address of ch1str
  150.                  mov  es, ax
  151.  
  152.                  mov  di, offset ch1str   ; offset address of ch1str
  153.                  mov  al, 0               ; try to match zero
  154.                  cld                      ; increment (DF = 0)
  155.  
  156.              string_end_loop:
  157.                  scasb
  158.                  jne  string_end_loop
  159.  
  160.                  dec  di                  ; back up one
  161.                  mov  ax, di
  162.                  sub  ax, offset ch1str
  163.                  call print_unsigned
  164.  
  165.              ;- - - - - - - - - - PUT CODE ABOVE THIS LINE
  166.  
  167.              In all these string operations, we need to be careful about
  168.              boundary conditions. What if there is no valid data? What if
  169.              there is one valid item? What if the string is empty?
  170.  
  171.              On exiting the loop, DI will point 1 past the first 0d, so we
  172.              need to back up one to point to the first 0d. Then subtracting
  173.              the starting position will give us the count.{5} Try it out and
  174.              find out how long it is. Since we now have 3 object modules, the
  175.              link instruction must read:
  176.  
  177.                  link  myprog+ch1str+asmhelp ;
  178.  
  179.              assuming that you name your program myprog.asm. Save the result
  180.              because we will need to use this number several times.
  181.  
  182.              ____________________
  183.  
  184.                 4. Just to keep it from being too easy, I have put garbage
  185.              both in front of the string and behind the string. That means
  186.              that the string length is shorter than the length of the object
  187.              file and the string does not start with the first byte of the
  188.              object file.
  189.  
  190.                 5. If the first byte in the string is 0d, we move one, then
  191.              move back one which gives the length zero.
  192.  
  193.  
  194.  
  195.  
  196.              The PC Assembler Tutor                                        194
  197.              ______________________
  198.  
  199.              You will notice that we have gotten the segment address by using
  200.              the SEG operator. You don't need to know the name of the segment.
  201.              The segment doesn't even have to be PUBLIC. As long as the
  202.              VARIABLE is either in the same file or is in another file and
  203.              PUBLIC, the linker will find the correct segment address and put
  204.              it there.
  205.  
  206.  
  207.              To make things a little more complicated, we will make another
  208.              infinite loop. This time you will enter a character, and the
  209.              program will find the first occurance of that character. We need
  210.              to add some error checking here. Since you will probably be
  211.              dreaming about taking your next vacation in Hawaii while you are
  212.              entering the data, a few characters that don't exist in the
  213.              string (things like G $ ? ~ ) might creep in. It would be
  214.              possible to run way past the end of the string before you found
  215.              that character. We'll put the length of the string (from the last
  216.              program) in CX, have a regular loop so we can't go too far, and
  217.              jump out of the loop if we find a match. 
  218.  
  219.              MYPROG2.ASM
  220.              ; - - - - - - - - - -
  221.              STRINGSTUFF  SEGMENT  PUBLIC 'DATA' 
  222.              EXTRN  ch1str:BYTE
  223.              STRINGSTUFF  ENDS
  224.              ; - - - - - - - - - - 
  225.              ;- - - - - - - - - - PUT CODE BELOW THIS LINE
  226.  
  227.                  mov  ax, seg ch1str
  228.                  mov  es, ax
  229.  
  230.              outer_loop:
  231.                  call get_ascii_byte           ; returns character in al
  232.                  mov  cx, $$$$$$$              ; enter string length here
  233.  
  234.                  mov  di, offset ch1str
  235.                  cld                           ; increment (DF = 0)
  236.  
  237.              string_end_loop:
  238.                  scasb
  239.                  je   after_loop               ; if equal, we found the char
  240.                  loop string_end_loop
  241.  
  242.                  mov  ax, 0                    ; we fell through the loop
  243.                  call print_unsigned
  244.                  jmp  outer_loop
  245.  
  246.              after_loop:
  247.                  mov  ax, di                   ; move for printing
  248.                  sub  ax, offset ch1str        ; number of bytes
  249.                  call print_unsigned
  250.  
  251.                  jmp  outer_loop
  252.  
  253.  
  254.              ;- - - - - - - - - - PUT CODE ABOVE THIS LINE
  255.  
  256.  
  257.  
  258.  
  259.  
  260.              Chapter 19 - Strings                                          195
  261.              ____________________
  262.  
  263.              Those dollar signs are the place to enter the exact length of the
  264.              string that you got from the last program. This time we jump out
  265.              of the loop if we find a match; DI will be 1 past the matching
  266.              character, but this will give us the right count (if we find the
  267.              character in the first space, we increment once). If we can't
  268.              find a match we fall through the loop and print a 0. Remember to
  269.              link all 3 modules when you run the program. Run the program and
  270.              then we'll move forward.
  271.  
  272.              This type of thing is so common with string operations that there
  273.              is a special prefix for SCAS and all other string operations
  274.              which makes the coding simpler. It has several forms:
  275.  
  276.                  rep       decrement cx ; repeat if cx is not zero
  277.                  repe      decrement cx ; repeat if cx not zero and zf = 1
  278.                  repz      decrement cx ; repeat if cx not zero and zf = 1
  279.                  repne     decrement cx ; repeat if cx not zero and zf = 0
  280.                  repnz     decrement cx ; repeat if cx not zero and zf = 0
  281.  
  282.              REP is for the move instructions which we will see later - it
  283.              won't work here. For each prefix, if either (or both) of the
  284.              conditions is not true, the repitition stops. For instance, with
  285.              REPE, if cx is zero, and/or if the comparison was not equal (so
  286.              the zero flag was not set), the instruction will stop. For our
  287.              program, the coding is:
  288.  
  289.                  repne scasb
  290.  
  291.              That's it. That replaces the whole inner loop. Here is our new
  292.              coding of the last program.
  293.  
  294.              MYPROG3.ASM
  295.              ; - - - - - - - - - -
  296.              STRINGSTUFF  SEGMENT  PUBLIC 'DATA' 
  297.              EXTRN  ch1str:BYTE
  298.              STRINGSTUFF  ENDS
  299.              ; - - - - - - - - - - 
  300.              ;- - - - - - - - - - PUT CODE BELOW THIS LINE
  301.  
  302.                  mov  ax, STRINGSTUFF
  303.                  mov  es, ax
  304.                  cld                           ; increment (DF = 0)
  305.  
  306.              outer_loop:
  307.                  call get_ascii_byte           ; returns character in al
  308.                  mov  cx, $$$$$$$              ; enter string length here
  309.                  lea  di, ch1str               ; address of string
  310.  
  311.                  repne  scasb
  312.  
  313.                  je   found_the_char           ; an equal comparison
  314.                  mov  ax, 0                    ; we didn't find a match
  315.                  call print_unsigned
  316.                  jmp  outer_loop
  317.  
  318.              found_the_char:
  319.                  mov  ax, di                   ; move for printing
  320.  
  321.  
  322.  
  323.  
  324.              The PC Assembler Tutor                                        196
  325.              ______________________
  326.  
  327.                  sub  ax, offset ch1str        ; number of bytes
  328.                  call print_unsigned
  329.                  jmp  outer_loop
  330.  
  331.              ;- - - - - - - - - - PUT CODE ABOVE THIS LINE
  332.  
  333.              There are two possibilities for exiting the 'repne scasb'
  334.              instruction. Either we found an equal comparison or we exhausted
  335.              all the characters in ch1str. If we found an equal comparison, JE
  336.              will send us to the print routine. Otherwise we print a 0 because
  337.              we finished the loop without finding anything. 
  338.  
  339.  
  340.              STOS
  341.  
  342.              We can ask the operating system to allocate memory for us while
  343.              the program is running.{6} When you get it, however, it will
  344.              contain trash. The fast way to clear it is to use STOS (store to
  345.              string). The instruction is:
  346.  
  347.                  stosb
  348.              or:
  349.                  stosw
  350.  
  351.              The equivalent action (not counting changing the value of DI) is:
  352.  
  353.                  mov  es:[di], ax    ; or AL for byte moves
  354.  
  355.              Once again (1) the pointer is the ES:DI pair, which is mandatory,
  356.              and (2) DI is incremented or decremented (by 1 for byte, by 2 for
  357.              word) depending on the status of DF, the direction flag. The
  358.              instruction moves a byte (a word) from the AL (AX) register to
  359.              the memory address pointed to by ES:DI. We can use the REP{7}
  360.              instruction to speed things up a bit. If we have a 11,872 word
  361.              block of memory, we can clear it with the following instructions:
  362.  
  363.              ; - - - - - - - 
  364.              DATASTUFF  SEGMENT
  365.              my_bufferdw  11872 dup (?)
  366.              DATASTUFF  ENDS
  367.              ; - - - - - - -
  368.  
  369.                  mov  ax, seg my_buffer
  370.                  mov  es, ax
  371.                  cld                           ; increment (DF = 0)
  372.  
  373.                  mov  ax, 0               ; clear the buffer with 0s
  374.                  mov  di, offset my_buffer
  375.                  mov  cx, 11872
  376.                  rep  stosw
  377.              ____________________
  378.  
  379.                 6. Cf. You-know-who's Programmer's Guide to You-know-what or
  380.              "DOS Programmer's Reference."
  381.  
  382.                 7. There is no comparison here, so REPE or REPNE doesn't make
  383.              any sense.
  384.  
  385.  
  386.  
  387.  
  388.              Chapter 19 - Strings                                          197
  389.              ____________________
  390.  
  391.  
  392.              That's as fast as it gets. Why does the STOS instruction use AX?
  393.              Because that's the register that port i/o uses. If you are
  394.              writing a communications program, you need speed. You can have
  395.              the following:
  396.  
  397.              ; - - - - - - - - - 
  398.              DATASTUFF  SEGMENT
  399.              port_address   dw   0F2A8h        ; this address is legal but
  400.                                                ; there's nothing there. 
  401.              input_buffer  db   4000h     dup (?)
  402.              output_buffer  db   4000h     dup (?)
  403.              DATASTUFF  ENDS
  404.              ; - - - - - - - - -
  405.  
  406.                  mov  ax, DATASTUFF
  407.                  mov  es, ax
  408.                  cld                           ; increment (DF = 0)
  409.                  mov  di, offset input_buffer
  410.                  mov  dx, port_address
  411.  
  412.              input_loop:
  413.                  in   al, dx
  414.                  stosb
  415.                  jmp  input_loop
  416.              ; - - - - - - - - - 
  417.  
  418.              A real program would be much more complicated because we would
  419.              have to check to see if data was ready to come in and we might
  420.              need to check the data for errors. Also we would occasionally
  421.              have to clear the buffer. The port address F2A8h is just an
  422.              arbitrary address. It's a legal address but there's nothing
  423.              there.
  424.  
  425.              We should write a program, so let's input a character and have it
  426.              fill the screen. We'll leave the last line of the screen alone so
  427.              you can see your input. Move your cursor to the last line before
  428.              beginning the program.
  429.  
  430.              ; - - - - - ENTER CODE BELOW THIS LINE
  431.  
  432.                  mov  ax, 0B800h          ; or 0B000h for a monochrome card
  433.                  mov  es, ax
  434.                  cld                      ; increment (DF = 0)
  435.  
  436.  
  437.              outer_loop:
  438.                  call get_ascii_byte      ; AL = fill char from input
  439.                  mov  ah, 07h             ; black background, white letters
  440.                  sub  di, di              ; set di to zero
  441.                  mov  cx, 1920            ; 24 lines X 80 chars
  442.                  rep  stosw
  443.                  jmp  outer_loop
  444.  
  445.              ; - - - - - ENTER CODE ABOVE THIS LINE
  446.  
  447.              If you have a monochrome card, the segment address is 0B000h. If
  448.  
  449.  
  450.  
  451.  
  452.              The PC Assembler Tutor                                        198
  453.              ______________________
  454.  
  455.              you have a color card and are in text mode, the segment address
  456.              should be 0B800h. This fills the first 24 lines with the input
  457.              character. The STOS instruction has no effect on the cursor. 
  458.  
  459.  
  460.              LODS
  461.  
  462.              The opposite of STOS is LODS (load string) It moves a byte (word)
  463.              from the string to the AL (AX) register. This time, for a change,
  464.              we use the SI register as a pointer, and the default register is
  465.              DS.{8} As always, SI is incremented or decremented by a byte
  466.              (word) depending on the setting of DF, the direction flag. The
  467.              two possibilities are:
  468.  
  469.                  lodsb
  470.              and
  471.                  lodsw 
  472.  
  473.              The equivalent action (not counting changing the value of SI) is:
  474.  
  475.                  mov  ax, [si]  ; or AL for byte moves
  476.  
  477.              This is an instruction for people that write device drivers. You
  478.              could use it if you are sending a string of characters to the
  479.              printer, but that's about it. Code for doing that would have the
  480.              following form:
  481.  
  482.              ; - - - - - - -
  483.              buffer   db   1000 dup (?)
  484.              ; - - - - - - -
  485.                  lea  si, buffer   ; the buffer must be in the ds segment
  486.                  cld                      ; increment
  487.  
  488.              out_loop:
  489.                  lodsb
  490.                  and  al, al              ; if 0, end of string
  491.                  jz   quit_loop
  492.  
  493.                  mov  dl, al              ; move character to dl {9}
  494.                  mov  ah, 5               ; int 21h function 5
  495.                  int  21h                 ; print a character
  496.                  jmp  out_loop
  497.  
  498.              quit_loop:
  499.                  ; continue with the program 
  500.  
  501.              If you actually run this program, many printers will not print
  502.              anything until they get an end of line signal ( 10d, 13d).
  503.  
  504.  
  505.              ____________________
  506.  
  507.                 8. Register DS can be overriden. We'll talk about that in the
  508.              second part of this chapter.
  509.  
  510.                 9. Int 21h (AH = 5) prints one character from DL to the
  511.              printer. Why it's DL and not AL is a mystery.
  512.  
  513.